import { computed, type ComputedRef, InjectionKey, markRaw, reactive, watch } from 'vue';
import { appConfig, BaseType, Data, IModal, Message, ModalProps, ModalState } from '../index';
import { clearObject } from '../util';


export interface ModalReturnProps {
  closable: ComputedRef<boolean | undefined>
  close: (...args: any) => Promise<void>
  closing: ComputedRef<boolean>
  modelValue: ComputedRef<boolean>
  ok: (...args: any) => Promise<void>
  open: (props: ModalProps) => void
  setClosable: (bol: boolean) => void
  setClose: (fn: () => Promise<void | false> | void | false) => void
  setTitle: (title: string) => void
  startCountdown: (timeout: number, cb: () => void) => void
  state: Data
  stopCountdown: () => void
  width: ComputedRef<BaseType | undefined>
}

export interface ModalContext {
  close: (...args: any) => Promise<void>
  closing: boolean
  lock: boolean
  ok: (...args: any) => Promise<void>
}

//@ts-ignore
export const Modal: IModal = {}

export const modalInjectionKey: InjectionKey<ModalContext> = Symbol('Modal')

const initialState = {
  closing: false,
  component: '',
  countdown: 0,
  lock: false,
  modelValue: false,
}

export const useModal = (): ModalReturnProps => {
  const state = reactive<ModalState>({ ...initialState })
  //region 倒计时
  let timer: any = 0
  const stopCountdown = (): void => {
    clearInterval(timer)
    state.countdown = 0
  }

  const startCountdown = (timeout = 5, cb: () => void): void => {
    if (state.countdown) {
      return // 没有结束禁止重复调用
    }
    state.countdown = timeout
    timer = setInterval((): void => {
      (state.countdown as number)--
      if ((state.countdown as number) <= 0) {
        stopCountdown()
        state.modelValue && cb?.()
      }
    }, 1000)
  }
  //endregion

  //region 设宽高
  const setWidthHeight = (props: ModalProps) => {
    const position = props.position ?? 'center'
    if (['top', 'bottom'].includes(position)) {
      state.width = undefined
      if (parseInt(String(props.height), 10) > 5) {
        state.height = props.height
      } else {
        state.height = appConfig.modal.widthOrHeight[position][props.height as number ?? appConfig.modal.defaultWidthOrHeight]
      }
    } else {
      state.height = undefined
      if (parseInt(String(props.width), 10) > 100) {
        state.width = props.width
      } else {
        state.width = appConfig.modal.widthOrHeight[position][props.width as number ?? appConfig.modal.defaultWidthOrHeight]
      }
    }
    return position
  }
  //endregion

  const open = (props: ModalProps): void => {
    Object.assign(clearObject(state), { ...props, ...initialState });
    stopCountdown()
    state.position = setWidthHeight(props)
    state.component = markRaw<any>(props.component)
    state.modelValue = true
  }

  //region 关闭
  async function afterCLose(isOk: boolean, args: any): Promise<void> {
    if (state.closing) return
    state.closing = true
    let result
    try {
      result = await state.close?.(isOk)
    } catch (e) {
      throw e
    } finally {
      state.closing = false
    }
    if (result !== false) {
      try {
        state.onClose?.(isOk, ...args)
      } finally {
        window.onbeforeunload = null
        state.modelValue = false
      }
    }
  }

  const close = async (...args: any): Promise<void> => {
    if (state.closable === false) return
    if (state.lock) {
      Message.warn('请先解锁后再关闭')
      return
    }
    await afterCLose(false, args);
  }

  const ok = async (...args: any): Promise<void> => {
    if (state.closable === false) return
    if (state.lock) {
      state.onClose?.(true, ...args)
      return
    }
    await afterCLose(true, args);
  }

  const setClose = (fn: (isOk: boolean) => Promise<void | false> | void | false): void => {
    state.close = fn
  }
  const setClosable = (bol: boolean): void => {
    state.closable = bol
  }
  const closable = computed(() => state.closable)
  watch(closable, val => {
    window.onbeforeunload = val ? null : (): boolean => true
  })
  const modelValue = computed(() => state.modelValue)
  //endregion

  const setTitle = (title: string): void => {
    state.title = title
  }
  const width = computed(() => state.width)
  const closing = computed(() => Boolean(state.closing))

  return {
    closable,
    close,
    closing,
    modelValue,
    ok,
    open,
    setClosable,
    setClose,
    setTitle,
    startCountdown,
    state,
    stopCountdown,
    width,
  }
}
